﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace CustomScrollBar.ListBoxScrollBar {
    /// <summary>
    /// ListBox垂直滚动条
    /// </summary>
    [ToolboxBitmap(typeof(VScrollBar))]
    public partial class ListBoxVScrollBar : UserControl {
        /// <summary>
        /// 构造函数
        /// </summary>
        public ListBoxVScrollBar() {
            InitializeComponent();
        }

        #region 私有成员
        private ListBox SListBox = null;
        private Color wakedColor = Color.FromArgb(((int)(((byte)(104)))), ((int)(((byte)(104)))), ((int)(((byte)(104)))));
        private Color pressedColor = Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(235)))), ((int)(((byte)(239)))));
        private Color enterColor = Color.FromArgb(((int)(((byte)(158)))), ((int)(((byte)(158)))), ((int)(((byte)(158)))));
        private double StepHeight = 6;
        private System.Timers.Timer TimeDelegate = null;
        private int TempCursor;
        private bool SlideBarFlag = false;
        private bool ScrollBarFlag = false;
        private int SlideBarOpacity = 255;
        private int MinHeight = 30;
        private double mSense = 0.2;

        private Image upWakeImage = null;
        private Image upEnterImage = null;
        private Image upPressedImage = null;

        private Image downWakeImage = null;
        private Image downEnterImage = null;
        private Image downPressedImage = null;

        /// <summary>
        /// 滑块按下标志
        /// </summary>
        private bool slideBarFlog;
        /// <summary>
        /// 滑块长按线程
        /// </summary>
        private Thread slideBarThread;
        /// <summary>
        /// 上箭头按下标志
        /// </summary>
        private bool upArrowFlog = false;
        /// <summary>
        /// 上箭头长按线程
        /// </summary>
        private Thread upArrowDownThread;
        /// <summary>
        /// 下箭头按下标志
        /// </summary>
        private bool downArrowFlog = false;
        /// <summary>
        /// 下箭头长按线程
        /// </summary>
        private Thread downArrowDownThread;

        #endregion

        #region  滑动灵敏度
        /// <summary>
        /// 滑动灵敏度,1到20之间
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue(null)]
        [Description("1到20之间")]
        public double Sense {
            set {
                mSense = 1 / value;
                this.StepHeight = this.SListBox.Font.Height * mSense;
            }
            get {
                return 1 / mSense;
            }
        }
        #endregion

        #region  滑动条最短长度
        /// <summary>
        /// 滑动条最短长度
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue(null)]
        [Description("滑动条最短长度")]
        public int MinSlideBarLenght {
            set {
                MinHeight = value;
            }
            get {
                return MinHeight;
            }
        }
        #endregion

        #region  要滑动的ListBox
        /// <summary>
        /// 要滑动的ListBox
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue(null)]
        [Description("要滚动的ListBox")]
        public ListBox ListBox {
            set {
                SListBox = value;
            }
            get {
                return SListBox;
            }
        }
        #endregion

        #region 滑动条被唤醒的颜色
        /// <summary>
        /// 滑动条被唤醒的颜色
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue(null)]
        public Color WakedColor {
            set {
                wakedColor = value;
            }
            get {
                return wakedColor;
            }
        }
        #endregion

        #region 滑动条被按下的颜色
        /// <summary>
        /// 滑动条被按下的颜色
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue(null)]
        [Description("滑动条被按下的颜色")]
        public Color PressedColor {
            set {
                pressedColor = value;
            }
            get {
                return pressedColor;
            }
        }
        #endregion

        #region 鼠标进入滑动条的颜色
        /// <summary>
        /// 鼠标进入滑动条的颜色
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue(null)]
        [Description("鼠标进入滑动条的颜色")]
        public Color EnterColor {
            set {
                enterColor = value;
            }
            get {
                return enterColor;
            }
        }
        #endregion

        #region 上箭头图标

        #region 上箭头的睡眠图标
        /// <summary>
        /// 上箭头的睡眠图标
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue("上箭头的睡眠图标")]
        public Image UpWakeImage {
            set {
                this.upWakeImage = value;
            }
            get {
                return this.upWakeImage;
            }
        }
        #endregion

        #region 上箭头的唤醒图标
        /// <summary>
        /// 上箭头唤醒图标
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue("上箭头的睡眠图标")]
        public Image UpEnterImage {
            set {
                this.upEnterImage = value;
            }
            get {
                return this.upEnterImage;
            }
        }
        #endregion

        #region 上箭头的激活图标
        /// <summary>
        /// 上箭头的激活图标
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue("上箭头的激活图标")]
        public Image UpPressedImage {
            set {
                this.upPressedImage = value;
            }
            get {
                return this.upPressedImage;
            }
        }
        #endregion

        #endregion

        #region 下箭头图标

        #region 下箭头的睡眠图标
        /// <summary>
        /// 下箭头的睡眠图标
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue("下箭头的睡眠图标")]
        public Image DownWakeImage {
            set {
                this.downWakeImage = value;
            }
            get {
                return this.downWakeImage;
            }
        }
        #endregion

        #region 下箭头的唤醒图标
        /// <summary>
        /// 下箭头的唤醒图标
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue("下箭头的唤醒图标")]
        public Image DownEnterImage {
            set {
                this.downEnterImage = value;
            }
            get {
                return this.downEnterImage;
            }
        }
        #endregion

        #region 下箭头的激活图标
        /// <summary>
        /// 下箭头的激活图标
        /// </summary>
        [Browsable(true)]
        [Localizable(true)]
        [Category("Appearance")]
        [DefaultValue("下箭头的激活图标")]
        public Image DownPressedImage {
            set {
                this.downPressedImage = value;
            }
            get {
                return this.downPressedImage;
            }
        }
        #endregion

        #endregion

        ///// <summary>
        ///// 获取滚动条信息
        ///// </summary>
        private Win32ApiLb.Scrollinfo ScrollInfo {
            get {
                Win32ApiLb.Scrollinfo si = new Win32ApiLb.Scrollinfo();
                si.cbSize = (uint)Marshal.SizeOf(si);
                si.fMask = (int)(Win32ApiLb.ScrollInfoMask.SIF_DISABLENOSCROLL | Win32ApiLb.ScrollInfoMask.SIF_ALL);
                Win32ApiLb.GetScrollInfo(this.ListBox.Handle, (int)Win32ApiLb.ScrollBarDirection.SB_VERT, ref si);
                return si;
            }
        }

        /// <summary>
        /// 创建滚动条
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void This_Created(object sender, EventArgs e) {
            this.SlideBar.Size = new Size(this.Width, 40);
            this.SlideBar.BackColor = this.BackColor;
            this.StepHeight = this.SListBox.Font.Height * mSense;
            this.upArrow.Image = upWakeImage;
            this.downArrow.Image = downWakeImage;
        }

        /// <summary>
        /// 计算滑块的高度
        /// </summary>
        /// <returns></returns>
        private int SlidebarHeight() {
            Win32ApiLb.Scrollinfo info = ScrollInfo;
            int max = info.nMax;
            int min = info.nMin;
            int page = (int)info.nPage;
            int ThumbHeight = page * (this.Height - this.upArrow.Height - this.downArrow.Height) / (max - min + 1);

            if (ThumbHeight < MinHeight) {
                ThumbHeight = MinHeight;
            }
            return ThumbHeight;
        }

        /// <summary>
        /// 计算滑块的Y位置
        /// </summary>
        /// <returns></returns>
        private int SlidebarPosY() {
            Win32ApiLb.Scrollinfo info = ScrollInfo;
            int trackPos = info.nTrackPos;
            int max = info.nMax;
            int min = info.nMin;
            int page = (int)info.nPage;

            int EmptyHeight = this.Height - this.upArrow.Height - this.downArrow.Height - SlidebarHeight(); //滚动条内空白区域的总高度
            int ThumbPosY = 0;
            if ((max - min + 1 - page) > 0) {
                ThumbPosY = EmptyHeight * trackPos / (max - min + 1 - page) + this.upArrow.Height;
            } else {
                ThumbPosY = EmptyHeight * trackPos + this.upArrow.Height;
            }
            return ThumbPosY;
        }

        /// <summary>
        /// 更新滚动条滑块长度和位置
        /// </summary>
        public void UpdateSlidebar() {
            SlideBar.Size = new Size(SlideBar.Width, SlidebarHeight());
            SlideBar.Location = new Point(SlideBar.Location.X, SlidebarPosY());
        }

        /// <summary>
        /// 鼠标进入滚动条事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScrollBar_MouseEnter(object sender, EventArgs e) {
            SlideBarFlag = true;
            this.UpdateSlidebar();
            this.SlideBar.BackColor = Color.FromArgb(((int)(((byte)(104)))), ((int)(((byte)(104)))), ((int)(((byte)(104)))));
        }

        /// <summary>
        /// 鼠标进入滑块事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SlideBar_MouseEnter(object sender, EventArgs e) {
            this.ScrollBarFlag = true;
            UpdateSlidebar();
            this.SlideBar.BackColor = EnterColor;
        }

        /// <summary>
        /// 离开滑块
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SlideBar_MouseLeave(object sender, EventArgs e) {
            this.SlideBar.BackColor = Color.FromArgb(((int)(((byte)(104)))), ((int)(((byte)(104)))), ((int)(((byte)(104)))));
        }

        /// <summary>
        /// 滑块按下拖动事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SlideBar_MouseDown(object sender, MouseEventArgs e) {
            this.SlideBar.BackColor = PressedColor;
            TempCursor = Cursor.Position.Y;
            slideBarFlog = true;
            Control.CheckForIllegalCrossThreadCalls = false;        //确保以线程安全方式访问控件
            slideBarThread = new Thread(() => {
                while (slideBarFlog) {
                    if (Cursor.Position.Y > TempCursor) {
                        if (Cursor.Position.Y >= TempCursor + StepHeight) {
                            TempCursor += (int)StepHeight;
                            if (this.SlideBar.Location.Y + (int)StepHeight <= this.Height - this.downArrow.Height - SlideBar.Height) //判断是否到达滑块滑动下限
                            {
                                this.SlideBar.Location = new Point(this.SlideBar.Location.X, this.SlideBar.Location.Y + (int)StepHeight);
                                double a = (double)GetControlScrollLocationMax() / (double)(this.Height - this.downArrow.Height - this.upArrow.Height - this.SlideBar.Height); //控件滚动条的最低位置与自定义滚动条的最低位置比例
                                SetControlScrollLocation(ScrollInfo, (int)((this.SlideBar.Location.Y - this.upArrow.Height) * a) + 1);
                            }
                        }
                    }
                    if (Cursor.Position.Y < TempCursor) {
                        if (Cursor.Position.Y <= TempCursor - StepHeight) {
                            TempCursor -= (int)StepHeight;
                            if (this.SlideBar.Location.Y - (int)StepHeight >= this.upArrow.Height)     //判断是否到达滑块滑动上限
                            {
                                this.SlideBar.Location = new Point(this.SlideBar.Location.X, this.SlideBar.Location.Y - (int)StepHeight);
                                double a = (double)GetControlScrollLocationMax() / (double)(this.Height - this.downArrow.Height - this.upArrow.Height - this.SlideBar.Height);   //控件滚动条的最低位置与自定义滚动条的最低位置比例
                                SetControlScrollLocation(ScrollInfo, (int)((this.SlideBar.Location.Y - this.upArrow.Height) * a));
                            }
                        }
                    }
                }
            });
            slideBarThread.IsBackground = true;
            slideBarThread.Start();
        }

        /// <summary>
        /// 设置控件的滚动条的位置
        /// </summary>
        /// <param name="scrollInfo"></param>
        /// <param name="pos"></param>
        private void SetControlScrollLocation(Win32ApiLb.Scrollinfo scrollInfo, int pos) {
            Win32ApiLb.Scrollinfo info = scrollInfo;
            info.nPos = pos;
            Win32ApiLb.SetScrollInfo(this.ListBox.Handle, (int)Win32ApiLb.ScrollBarDirection.SB_VERT, ref info, true);
            IntPtr aa = new IntPtr(Win32ApiLb.MakeLong((short)Win32ApiLb.SB_THUMBTRACK, (short)(info.nPos)));
            Win32ApiLb.SendMessage(this.SListBox.Handle, Win32ApiLb.WM_VSCROLL, aa, new IntPtr());
        }

        /// <summary>
        /// 获取控件滚动条滑动到最下端的位置
        /// </summary>
        /// <returns></returns>
        private int GetControlScrollLocationMax() {
            Win32ApiLb.Scrollinfo info = ScrollInfo;
            int max = info.nMax;
            int min = info.nMin;
            int page = (int)info.nPage;
            int posmax = max - min + 1 - page;
            return posmax;
        }

        /// <summary>
        /// 滑块松开事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SlideBar_MouseUp(object sender, MouseEventArgs e) {
            this.SlideBar.BackColor = EnterColor;
            slideBarFlog = false;
            slideBarThread.Join();
        }

        /// <summary>
        /// 上箭头鼠标点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void upArrow_MouseClick(object sender, MouseEventArgs e) {
            if (this.SlideBar.Location.Y - (int)StepHeight >= this.upArrow.Height) {
                this.SlideBar.Location = new Point(this.SlideBar.Location.X, this.SlideBar.Location.Y - (int)StepHeight);
                double a = (double)GetControlScrollLocationMax() / (double)(this.Height - this.downArrow.Height - this.upArrow.Height - this.SlideBar.Height);   //控件滚动条的最低位置与自定义滚动条的最低位置比例
                SetControlScrollLocation(ScrollInfo, (int)((this.SlideBar.Location.Y - this.upArrow.Height) * a));
            }
        }

        /// <summary>
        /// 下箭头鼠标点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downArrow_MouseClick(object sender, MouseEventArgs e) {
            if (this.SlideBar.Location.Y + (int)StepHeight <= this.Height - this.downArrow.Height - SlideBar.Height) {
                this.SlideBar.Location = new Point(this.SlideBar.Location.X, this.SlideBar.Location.Y + (int)StepHeight);
                double a = (double)GetControlScrollLocationMax() / (double)(this.Height - this.downArrow.Height - this.upArrow.Height - this.SlideBar.Height);   //控件滚动条的最低位置与自定义滚动条的最低位置比例
                SetControlScrollLocation(ScrollInfo, (int)((this.SlideBar.Location.Y) * a));
            }
        }

        /// <summary>
        /// 上箭头鼠标长按事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void upArrow_MouseDown(object sender, MouseEventArgs e) {
            this.upArrow.Image = this.upPressedImage;
            upArrowFlog = true;
            Control.CheckForIllegalCrossThreadCalls = false;        //确保以线程安全方式访问控件
            upArrowDownThread = new Thread(() => {
                while (this.upArrowFlog != false) {
                    if (this.SlideBar.Location.Y - (int)StepHeight >= this.upArrow.Height) {
                        this.SlideBar.Location = new Point(this.SlideBar.Location.X, this.SlideBar.Location.Y - (int)StepHeight);
                        double a = (double)GetControlScrollLocationMax() / (double)(this.Height - this.downArrow.Height - this.upArrow.Height - this.SlideBar.Height);   //控件滚动条的最低位置与自定义滚动条的最低位置比例
                        SetControlScrollLocation(ScrollInfo, (int)((this.SlideBar.Location.Y - this.upArrow.Height) * a));
                    }
                    Thread.Sleep(50);
                }
            });
            upArrowDownThread.IsBackground = true;
            upArrowDownThread.Start();
        }

        /// <summary>
        /// 上箭头鼠标抬起终止长按事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void upArrow_MouseUp(object sender, MouseEventArgs e) {
            this.upArrow.Image = this.upEnterImage;
            upArrowFlog = false;
            upArrowDownThread.Join();
        }

        /// <summary>
        /// 下箭头鼠标长按事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downArrow_MouseDown(object sender, MouseEventArgs e) {
            this.downArrow.Image = this.downPressedImage;
            downArrowFlog = true;
            Control.CheckForIllegalCrossThreadCalls = false;        //确保以线程安全方式访问控件
            downArrowDownThread = new Thread(() => {
                while (this.downArrowFlog != false) {
                    if (this.SlideBar.Location.Y + (int)StepHeight <= this.Height - this.downArrow.Height - SlideBar.Height) {
                        this.SlideBar.Location = new Point(this.SlideBar.Location.X, this.SlideBar.Location.Y + (int)StepHeight);
                        double a = (double)GetControlScrollLocationMax() / (double)(this.Height - this.downArrow.Height - this.upArrow.Height - this.SlideBar.Height);   //控件滚动条的最低位置与自定义滚动条的最低位置比例
                        SetControlScrollLocation(ScrollInfo, (int)((this.SlideBar.Location.Y) * a));
                    }
                    Thread.Sleep(50);
                }
            });
            downArrowDownThread.IsBackground = true;
            downArrowDownThread.Start();
        }

        /// <summary>
        /// 下箭头鼠标抬起终止长按事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downArrow_MouseUp(object sender, MouseEventArgs e) {
            this.downArrow.Image = this.downEnterImage;
            downArrowFlog = false;
            downArrowDownThread.Join();
        }

        /// <summary>
        /// 下箭头鼠标进入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void upArrow_MouseEnter(object sender, EventArgs e) {
            this.upArrow.Image = this.upEnterImage;
        }

        /// <summary>
        /// 下箭头鼠标离开
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void upArrow_MouseLeave(object sender, EventArgs e) {
            this.upArrow.Image = this.upWakeImage;
        }

        /// <summary>
        /// 上箭头鼠标进入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downArrow_MouseEnter(object sender, EventArgs e) {
            this.downArrow.Image = this.downEnterImage;
        }

        /// <summary>
        /// 上箭头鼠标离开
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downArrow_MouseLeave(object sender, EventArgs e) {
            this.downArrow.Image = this.downWakeImage;
        }
    }

    /// <summary>
    /// Win32API
    /// </summary>
    public class Win32ApiLb { 
        [StructLayout(LayoutKind.Sequential)]
        public struct TagScrollinfo {
            public uint cbSize;
            public uint fMask;
            public int nMin;
            public int nMax;
            public uint nPage;
            public int nPos;
            public int nTrackPos;
        }

        public enum fnBar {
            SB_HORZ = 0,
            SB_VERT = 1,
            SB_CTL = 2
        }

        public enum fMask {
            SIF_ALL,
            SIF_DISABLENOSCROLL = 0X0010,
            SIF_PAGE = 0X0002,
            SIF_POS = 0X0004,
            SIF_RANGE = 0X0001,
            SIF_TRACKPOS = 0X0008
        }

        public static int MakeLong(short lowPart, short highPart) {
            return (int)(((ushort)lowPart) | (uint)(highPart << 16));
        }

        public const int SB_THUMBTRACK = 5;
        public const int WM_HSCROLL = 0x114;
        public const int WM_VSCROLL = 0x115;

        [DllImport("user32.dll", EntryPoint = "GetScrollInfo")]
        public static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref Scrollinfo lpsi);

        [DllImport("user32.dll", EntryPoint = "SetScrollInfo")]
        public static extern int SetScrollInfo(IntPtr hwnd, int fnBar, [In] ref Scrollinfo lpsi, bool fRedraw);

        [DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        //[DllImport("user32.dll", SetLastError = true)]
        [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
        public static extern bool PostMessage(IntPtr hWnd, uint Msg, long wParam, int lParam);

        public struct Scrollinfo {
            public uint cbSize;
            public uint fMask;
            public int nMin;
            public int nMax;
            public uint nPage;
            public int nPos;
            public int nTrackPos;
        }

        public enum ScrollInfoMask {
            SIF_RANGE = 0x1,
            SIF_PAGE = 0x2,
            SIF_POS = 0x4,
            SIF_DISABLENOSCROLL = 0x8,
            SIF_TRACKPOS = 0x10,
            SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS
        }

        public enum ScrollBarDirection {
            SB_HORZ = 0,
            SB_VERT = 1,
            SB_CTL = 2,
            SB_BOTH = 3
        }
    }
}


